﻿using System;
using System.Diagnostics;

using Windows.ApplicationModel;
using Windows.ApplicationModel.Activation;
using Windows.UI.Core;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Navigation;

using CalciumTemplateApp.UI;

using Outcoder;
using Outcoder.ApplicationModel;
using Outcoder.ComponentModel;
using Outcoder.ComponentModel.StatePreservation;

namespace CalciumTemplateApp
{
    /// <summary>
    /// Provides application-specific behavior to supplement the default Application class.
    /// </summary>
    sealed partial class App : Application
    {
        /// <summary>
        /// Initializes the singleton application object.  This is the first line of authored code
        /// executed, and as such is the logical equivalent of main() or WinMain().
        /// </summary>
        public App()
        {
			Microsoft.ApplicationInsights.WindowsAppInitializer.InitializeAsync(
				Microsoft.ApplicationInsights.WindowsCollectors.Metadata |
				Microsoft.ApplicationInsights.WindowsCollectors.Session);

	        
			InitializeComponent();

			Suspending += HandleSuspending;
        }

	    void RunBootStrapper(bool appWasTombstoned)
	    {
			try
			{
				var bootstrapper = new Bootstrapper
					{
						RestoreTransientState = appWasTombstoned
					};
				bootstrapper.Run();
			}
			catch (Exception ex)
			{
				if (Debugger.IsAttached)
				{
					Debug.WriteLine("Bootstrapper raised exception", ex);
					Debugger.Break();
				}

				throw;
			}
		}

        /// <summary>
        /// Invoked when the application is launched normally by the end user.  Other entry points
        /// will be used such as when the application is launched to open a specific file.
        /// </summary>
        /// <param name="e">Details about the launch request and process.</param>
        protected override void OnLaunched(LaunchActivatedEventArgs e)
        {

#if DEBUG
            if (System.Diagnostics.Debugger.IsAttached)
            {
                this.DebugSettings.EnableFrameRateCounter = true;
            }
#endif

            Frame rootFrame = Window.Current.Content as Frame;

	        bool appWasTombstoned = false;

			// Do not repeat app initialization when the Window already has content,
			// just ensure that the window is active
			if (rootFrame == null)
            {
                rootFrame = new Frame();

                rootFrame.NavigationFailed += HandleNavigationFailed;
				rootFrame.Navigated += HandleNavigated;

				if (e.PreviousExecutionState == ApplicationExecutionState.Terminated)
                {
	                appWasTombstoned = true;
                }

                Window.Current.Content = rootFrame;

	            var navigationManager = SystemNavigationManager.GetForCurrentView();
				navigationManager.BackRequested += HandleBackRequested;

				navigationManager.AppViewBackButtonVisibility =
					rootFrame.CanGoBack ?
					AppViewBackButtonVisibility.Visible :
					AppViewBackButtonVisibility.Collapsed;
			}

			RunBootStrapper(appWasTombstoned);

            if (rootFrame.Content == null)
            {
                // When the navigation stack isn't restored navigate to the first page,
                // configuring the new page by passing required information as a navigation
                // parameter
                rootFrame.Navigate(typeof(MainView), e.Arguments);
            }
            // Ensure the current window is active
            Window.Current.Activate();

			var messenger = Dependency.Resolve<IMessenger>();

			if (appWasTombstoned)
			{	/* App was Tombstoned. */
				messenger.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Launching));

				var transientState = Dependency.Resolve<ITransientState>();
				
				messenger.Publish(new LoadStateRequest(
										   ApplicationStateTypes.Persistent | ApplicationStateTypes.Transient,
										   transientState.StateDictionary));
			}
			else if (e.PreviousExecutionState == ApplicationExecutionState.ClosedByUser
			         || e.PreviousExecutionState == ApplicationExecutionState.NotRunning)
			{
				messenger.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Launching));

				messenger.Publish(new LoadStateRequest(ApplicationStateTypes.Persistent, null));
			}
			else if (e.PreviousExecutionState == ApplicationExecutionState.Running
					 || e.PreviousExecutionState == ApplicationExecutionState.Suspended)
			{
				messenger.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Activated));
			}
		}

        /// <summary>
        /// Invoked when Navigation to a certain page fails
        /// </summary>
        /// <param name="sender">The Frame which failed navigation</param>
        /// <param name="e">Details about the navigation failure</param>
        void HandleNavigationFailed(object sender, NavigationFailedEventArgs e)
        {
            throw new Exception("Failed to load Page " + e.SourcePageType.FullName);
        }

        /// <summary>
        /// Invoked when application execution is being suspended.  Application state is saved
        /// without knowing whether the application will be terminated or resumed with the contents
        /// of memory still intact.
        /// </summary>
        /// <param name="sender">The source of the suspend request.</param>
        /// <param name="e">Details about the suspend request.</param>
        async void HandleSuspending(object sender, SuspendingEventArgs e)
        {
            var deferral = e.SuspendingOperation.GetDeferral();

			var messenger = Dependency.Resolve<IMessenger>();

			await messenger.Publish(new ApplicationLifeCycleMessage(ApplicationLifeCycleState.Deactivated));

			var transientState = Dependency.Resolve<ITransientState>();

			await messenger.Publish(new SaveStateRequest(
									   ApplicationStateTypes.Persistent | ApplicationStateTypes.Transient,
									   transientState.StateDictionary));

			deferral.Complete();
        }

		void HandleNavigated(object sender, NavigationEventArgs e)
		{
			Frame frame = (Frame)sender;
			/* Update the Back button's visibility. */
			SystemNavigationManager.GetForCurrentView().AppViewBackButtonVisibility =
				frame.CanGoBack ?
				AppViewBackButtonVisibility.Visible :
				AppViewBackButtonVisibility.Collapsed;
		}

		void HandleBackRequested(object sender, BackRequestedEventArgs e)
		{
			Frame rootFrame = (Frame)Window.Current.Content;

			if (rootFrame.CanGoBack)
			{
				e.Handled = true;
				rootFrame.GoBack();
			}
		}
	}
}
